
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <math.h>	// pow()

#include "socket.h"
#include "getarchive.h"
#include "Ccitt.h"

#define MAXSIZE 8192
#define MAXCMD 64
#define CRCMAXCOUNT 4	//最多允许crc重试次数


#if HOST_IS_BIGENDIAN
#define SHORT_SWAP(x)           (((x << 8) & 0xFF00) | ((x >> 8) & 0x00FF))
#else
#define SHORT_SWAP(x)           (x)
#endif

// packed time utilities
#define EXTRACT_PACKED_YEAR(x)          ((((x) >> 9) & 0x3F) + 2000)
#define EXTRACT_PACKED_MONTH(x)         (((x) >> 5) & 0xF)
#define EXTRACT_PACKED_DAY(x)           ((x) & 0x1F)
#define EXTRACT_PACKED_HOUR(y)          ((y) / 100)
#define EXTRACT_PACKED_MINUTE(y)        ((y) % 100)


// 大小端转换
#if HOST_IS_BIGENDIAN
static void swapArchiveRecord (ARCHIVE_RECORD *rec)
{
    rec->date = SHORT_SWAP(rec->date);
    rec->time = SHORT_SWAP(rec->time);
    rec->outTemp = SHORT_SWAP(rec->outTemp);
    rec->highOutTemp = SHORT_SWAP(rec->highOutTemp);
    rec->lowOutTemp = SHORT_SWAP(rec->lowOutTemp);
    rec->rain = SHORT_SWAP(rec->rain);
    rec->highRainRate = SHORT_SWAP(rec->highRainRate);
    rec->barometer = SHORT_SWAP(rec->barometer);
    rec->radiation = SHORT_SWAP(rec->radiation);
    rec->windSamples = SHORT_SWAP(rec->windSamples);
    rec->inTemp = SHORT_SWAP(rec->inTemp);

    return;
}
#else
static void swapArchiveRecord (ARCHIVE_RECORD *rec)
{
    return;
}
#endif

// 完成将字串指令末尾加上davis协议特有的\x0a，并通过socket发出去
int sendcmd(int serverfd, char * cmd)
{
	int cmdlen = 0;
	char str[MAXCMD];
	memset(str, '\0', MAXCMD);
	memcpy(str, cmd, strlen(cmd));
	str[strlen(cmd)] = '\x0a';
	cmdlen = strlen(cmd)+1;
	retcode = TCPWrite(serverfd, cmd, cmdlen);
	if (retcode < 0)
	{
		printf("main: send cmd [%s] failed [%d]\n", cmd, retcode);
		return -4;
	}
	printf("main: cmd [%s] sent\n", cmd);
	return cmdlen;
}

// 读len个字节的数据并校验crc
int readWithCRC (int serverfd, void *bfr, int len)
{
    int     retVal, index = 0;
    unsigned char   *ptr = (unsigned char *)bfr;
    unsigned short  crc = 0;

    //retVal = (*work->medium.read) (&work->medium, bfr, len, msTimeout);
    retVal = TCPBlockRead(serverfd, (char*)bfr, len);
    if (retVal != len)
    {
        return -1;
    }

    for (index = 0; index < len; index ++)
    {
        crc =  crc_table [(crc >> 8) ^ ptr[index]] ^ (crc << 8);
    }

    return (crc == 0) ? (len) : (-2);
}

// 对数据块生成crc
unsigned short genCRC (void *data, int length)
{
    unsigned char           *ptr = (unsigned char *)data;
    unsigned short          crc = 0;
    register int    i;

    for (i = 0; i < length; i ++)
    {
        crc =  crc_table[(crc >> 8) ^ ptr[i]] ^ (crc << 8);
    }

    crc = ((crc >> 8) & 0xFF) | ((crc << 8) & 0xFF00);
    return crc;
}

// 把二进制数据末尾加上crc校验码，并通过socket发出去
int writeWithCRC (int serverfd, void *buffer, int length)
{
    int     retVal;
    int     wrerrno = 0;
    unsigned short  crc;

    crc = SHORT_SWAP(genCRC (buffer, length));

    //retVal = (*work->medium.write) (&work->medium, buffer, length);
    retVal = TCPWrite(serverfd, buffer, length);
    if (retVal != length)
    {
        if (retVal == -1)
        {
            wrerrno = errno;
        }

        return -1;
    }
    //retVal = (*work->medium.write) (&work->medium, &crc, 2);
    retVal = TCPWrite(serverfd, &crc, 2);
    if (retVal != 2)
    {
        if (retVal == -1)
        {
            wrerrno = errno;
        }

        return -2;
    }

    /*  ... now wait for the TX buffer to empty out (this blocks)
    */
    //(*work->medium.txdrain) (&work->medium);

    return length;
}

// 唤醒console
int wakeup(int serverfd)
{
	char wake[10];
	memset(wake, '\0', 10);
	printf("wake up!\n");
	if (TCPWrite(serverfd, "\x0a", 1) < 0)
	{
		printf("main: tcp write failed!\n");
		return -2;
	}
	if (TCPBlockRead(serverfd, wake, 10)<0)
	{
		printf("main: tcp read failed!\n");
		return -3;
	}
	printf("Wakup! %s\n", wake);
	return 0;
}

// 取ack
int getack(int serverfd)
{
	char ack = 0;
	if (TCPBlockRead(serverfd, &ack, 1) < 0)
	{
		printf("main: receive data failed\n");
		return -1;
	}
	printf("read [%x]\n", (int)ack);
	if (ack != 0x06)	//ack
	{
		printf("cmd not recognized \n");
		return -2;
	}
	return 0;
}

// * Calculated Weather Data
//  calculate the heat index
float wvutilsCalculateHeatIndex (float temp, float humidity)
{
    double      T1, T2, T3, T4, T5, T6, T7, T8, T9, result;

    if (temp < 75.0)
    {
        return temp;
    }

    T1 = -42.379;
    T2 = 2.04901523 * temp;
    T3 = 10.14333127 * humidity;
    T4 = -0.22475541 * temp * humidity;
    T5 = -6.83783E-3 * (temp * temp);
    T6 = -5.481717E-2 * (humidity * humidity);
    T7 = 1.22874E-3 * (temp * temp) * humidity;
    T8 = 8.5282E-4 * temp * (humidity * humidity);
    T9 = -1.99E-6 * (temp * temp) * (humidity * humidity);
    result = T1 + T2 + T3 + T4 + T5 + T6 + T7 + T8 + T9;

    return (float)result;
}

//  calculate the wind chill
float wvutilsCalculateWindChill (float temp, float windspeed)
{
    double      T1, T2, T3, T4;
    double      result;

    if (temp >= 50.0 || windspeed <= 3.0)
    {
        return temp;
    }

    T1 = 35.74;
    T2 = 0.6215 * temp;
    T3 = -1.0 * (35.75 * pow (windspeed, 0.16));
    T4 = 0.4275 * temp * pow (windspeed, 0.16);
    result = T1 + T2 + T3 + T4;

    return (float)result;
}

//  calculate the dewpoint
float wvutilsCalculateDewpoint (float temp, float humidity)
{
    float       Tc  = (5.0/9.0)*(temp - 32.0);
    float       Es  = 6.11 * pow ((double)10.0, (double)(7.5 * (Tc/(237.7 + Tc))));
    float       E   = (humidity * Es)/100;
    float       Tdc = (-430.22 + 237.7 * log(E))/(-log(E)+19.08);
    return ((9.0/5.0) * Tdc + 32);
}

// 把每条记录中的数据解析出来写入downld02.txt文件中，
// 并把最新记录时间记录下来
// 参阅convertToArchivePkt这个函数
// 不要看getarchive.c中写的parse()函数
int record(ARCHIVE_RECORD * newRecord, FILE *downld02, int timespan, struct tm bknTime)
{
	int	year, month, day, hour, minute, dayMinutes;
	float	chill, dew, heat;
	//struct tm       bknTime;
	float outtemp, outhumidity, winspeed;
	unsigned short datestamp;
	unsigned short timestamp;
	char sstamp[4];

	// create the time_t time for the record:
	bknTime.tm_year  = EXTRACT_PACKED_YEAR(newRecord->date) - 1900;
	bknTime.tm_mon   = EXTRACT_PACKED_MONTH(newRecord->date) - 1;
	bknTime.tm_mday  = EXTRACT_PACKED_DAY(newRecord->date);
	bknTime.tm_hour  = EXTRACT_PACKED_HOUR(newRecord->time);
	bknTime.tm_min   = EXTRACT_PACKED_MINUTE(newRecord->time);
	bknTime.tm_sec   = 0;
	bknTime.tm_isdst = -1;
	
	// 按照格式把各项数据转换单位，写入downld02.txt
/*
                  Temp     Hi    Low   Out    Dew  Wind  Wind   Wind    Hi    Hi   Wind   Heat    THW                Rain    Heat    Cool    In     In    In     In   Wind  Wind    ISS   Arc.
  日期    时间     Out   Temp   Temp   Hum    Pt. Speed   Dir    Run Speed   Dir  Chill  Index  Index   Bar    Rain  Rate    D-D     D-D    Temp   Hum    Dew   Heat  Samp   Tx   Recept  Int.
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
09-02-26   9:17    ---    ---    ---   ---    ---   0.0   ---   0.00   0.0   ---    ---    ---    ---  1015.1  0.00   0.0     ---     ---   21.5    23   -0.5   19.3     0    1      0.0    1 
*/
	// time 09-02-26   9:17    
	fprintf(downld02, "%02d-%02d-%02d  ", bknTime.tm_year, bknTime.tm_mon, bknTime.tm_mday);
	fprintf(downld02, "%2d:%2d    ", bknTime.tm_hour, bknTime.tm_min);
	// temp out---    
	if (newRecord->outTemp != 0x7FFF)	// exclude dash value 32767
		{outtemp = (float)newRecord->outTemp/10.0;
		fprintf(downld02, "%3f    ", outtemp);}
	else	fprintf(downld02, "---    ");
	// out hi temp---    
	if (newRecord->highOutTemp != 0x8000)	// exclude dash value -32768
		fprintf(downld02, "%3f    ", (float)newRecord->highOutTemp/10.0);
	else	fprintf(downld02, "---    ");
	// out low temp---   
	if (newRecord->lowOutTemp != 0x7FFF)	// exclude dash value 32767
		fprintf(downld02, "%3f   ", (float)newRecord->lowOutTemp/10.0);
	else	fprintf(downld02, "---   ");
	// out humidity---    
	if (newRecord->outHumidity != 0xFF)	// exclude dash walue 255
		{outhumidity = (float)newRecord->outHumidity);
		fprintf(downld02, "%3f    ", outhumidity);}
	else	fprintf(downld02, "---    ");
	// out dew point---   
	if (newRecord->outHumidity != 0xFF)	// exclude dash walue 255
		{dew = wvutilsCalculateDewpoint (outtemp, outhumidity);
		fprintf(downld02, "%3f   ", dew);}
	else	fprintf(downld02, "---   ");
	// out wind speed0.0   
	if (newRecord->avgWindSpeed != 0xFF)	// exclude dash walue 255
		{ winspeed = (float)newRecord->avgWindSpeed;
		fprintf(downld02, "%2f   ", winspeed);}
	else	fprintf(downld02, "0.0   ");
	// wind direction---   
	if (newRecord->prevWindDir < 16)	// include the only 16 possible values
		fprintf(downld02, "%3f   "(float)newRecord->prevWindDir * 22.5);
	else	fprintf(downld02, "---   ");
	// wind run0.00   
	if (newRecord->avgWindSpeed != 0xFF)	// exclude dash walue 255
		fprintf(downld02, "%3f   ", (float)newRecord->avgWindSpeed * timespan / 60);
	else	fprintf(downld02, "0.00   ");
	// hi speed0.0  // as the dash value is just 0 too, needless to exclude it
	fprintf(downld02, "%2f   ", (float)newRecord->highWindSpeed);
	// hi direction---    
	if (newRecord->highWindDir < 16)	// include the only 16 possible valuse
		fpirntf(downld02, "%3f    ", (float)newRecord->highWindDir);
	else	fprintf(downld02, "---    ");
	// wind chill---    
	if ((newRecord->outTemp != 0x7FFF) && (newRecord->avgWindSpeed != 0xFF))
		fprintf(downld02, "%3f    ", wvutilsCalculateWindChill(outtemp, winspeed)):
	else	fprintf(downld02, "---    ");
	// heat index---    
	if ((newRecord->outTemp != 0x7FFF) && (newRecord->outHumidity != 0xFF))
		fprintf(downld02, "%3f    ", wvutilsCalculateHeatIndex(outtemp, outhumidity));
	else	fprintf(downld02, "---    ");
	// THW index	the calculation not known
	fprintf(downld02, "---  ");
	// Bar1015.1  // as the dash value is just 0 too, needless to exclude it
	fprintf(downld02, "%6f  ", (float)newRecord->barometer/1000.0);
	// Rain0.00   // as the dash value is just 0 too, needless to exclude it
	/*
	Integrated Sensor Suite Installation Manual.pdf:
		The rain collector tipping bucket mechanism contains a standard measurement weight
magnet that takes measurements in 0.01''. The ISS hardware kit contains a metric adapter
that adjusts the weight of the tipping bucket mechanism so that it reports 0.2 mm metric
measurements for every tip of the bucket. To install the metric adapter:
	vproInterface.c, line 1457 to 1490
	conclusion: 127 ticks per inch
	*/
	fprintf(downld02, "%3f   ", ((float)(newRecord->rain & 0xFFF))/127);
	// rain rate0.0     // as the dash value is just 0 too, needless to exclude it
	fprintf(downld02, "%2f     ", (float)newRecord->highRainRate/127);
	// Heat D-D---     // nothing
	fprintf(downld02, "0.000   ");
	// cool D-D0.000   // nothing
	fprintf(downld02, "0.000   ");
	// in temp22.4    
	if (newRecord->inTemp != 0x7FFF)		// exclude dash value 32767
		fprintf(downld02, "%3f    ", (float)newRecord->inTemp/10.0);
	fprintf(downld02, "---    ", 0.0);
	// in hum60   //
	if (newRecord->inHumidity != 0xFF)	// exclude dash walue 255
		fprintf(downld02, "%2f  ", (float)newRecord->inHumidity);
	fprintf(downld02, "--  ");
	// in due
	if ((newRecord->inTemp != 0x7FFF) && (newRecord->inHumidity != 0xFF))
		fprintf(downld02, "%4f   ", 
			wvutilsCalculateDewpoint((float)newRecord->inTemp/10.0, (float)newRecord->inHumidity));
	fprintf(downld02, "----   ");
	// in heat22.4   // in heat index
	if ((newRecord->inTemp != 0x7FFF) && (newRecord->inHumidity != 0xFF))
		fpirntf(downld02, "%4f   ", 
			wvutilsCalculateHeatIndex((float)newRecord->inTemp/10.0, (float)newRecord->inHumidity));
	fprintf(downld02, "---   ");
	// Wind sample234    
	fprintf(downld02, "%3d    ", newRecord->windSamples);
	// Wind tx	not known, nothing
	fprintf(downld02, "1      ");
	// iss recept	not known, nothing
	fpirntf(downld02, "0.0    ");
	// arc int	not known, nothing
	fprintf(downld02, "1 ");
	// next line
	fpirntf(downld02, "\n");
	
	// calculate day and time stamp
	datestamp = ((bknTime.tm_year-1900) << 9) | (bknTime.tm_mon << 5) | (bknTime.tm_mday);
	timestamp = bknTime.tm_hour*100 + bknTime.tm_min;
	memcpy(sstamp, datestamp, 2);
	memcpy(sstamp[2], timespan, 2);
		
		
	return 0;
}

int updatetimestamp(ARCHIVE_PAGE * newRecord, FILE *timefile)
{
	int i = 0;
}

// 看vproInterface.c中的这个函数最有价值
// vpifReadMessage()
// static int processArchivePage (WVIEWD_WORK *work, ARCHIVE_PAGE *page)

// blabla 127.0.0.1 4001 fisrt
// blabla 127.0.0.1 4001 last
// blabla 127.0.0.1 4001
// 可以置于uclinux中，也可以置于i386中，用来网口取archive
// 返回值是davis的console的archive的record存档时间，
// 以便于bash程序计划下次调度此函数
/*
大改动：分成3只函数，第一次初始化使用dmp指令下载全部record，main调用dmpsend函数
随后再被bash入口main则调用dmpaftsend函数使用dmpaft指令下载更新record
更新依据的判断，解析record纪录时把时间量攒为时间戳格式，每条record解析，时刻保存最新
时间戳，以该时间戳加上crc校验值发送取得更新记录
更新时间的判断，第一次初始化时使dmp下载全部时，解析出相邻两record的时间差，记录下来，返回给bash，
bash依据此值，周期调度此main
*/
int dmpsend()
{
}
int dmpaftsend()
{
}
int main(int argc, char* argv[])
{
	int sopo = 4001;
	int serverfd;
	int retcode;
	char cmd[MAXCMD];
	int cmdlen;
	char data[MAXSIZE];
	ARCHIVE_PAGE * onepage;
	int datalength;
	FILE *timefile;
	FILE *downld02;
	char sequencenumber;
	int crccount;
	int i;
	int timespan = 0;
	struct tm bknTime;
	
	sopo = atoi( argv[2] );
	
	// init media
	retcode = TCPClientInit(argv[1], sopo, &serverfd);
	if (retcode < 0)
	{
		printf("main: tcp init failed [%d]\n", retcode);
		return -1;
	}
	printf("main: tcp init ok\n");
	
	// wakeup
	if (wakeup(serverfd) < 0)
		return -2;
		
	// open downld02.txt for write
	downld02 = fopen("downld02.txt", "w");
	if (downld02 == NULL)	//必须有一个初始文件存有文件头部分，这里只负责写数据
	{
		printf("no downld02.txt found!\n");
		return -9;
	}
	
	// get archive
	// 流程：
	// 第一次运行创建时间绰文件stamp并使用dmp下载全部记录
	// 如果存在时间绰文件stamp则不是第一次运行，调用dmpafter下载更新记录
	timefile = fopen("stamp", "rb");
	if (timefile == NULL)
	{
		/*	第一次，dmp所有记录
		>"DMP"<LF>
		<<ACK>
		<<0x00>
		<<52 byte data record 0>
		<<52 byte data record 1>
		<<52 byte data record 2>
		<<52 byte data record 3>
		<<52 byte data record 4>
		<<4 unused bytes>
		<<2 byte CRC>
		-- At this point verify the CRC and send either <ACK>, <0x21>, or <ESC>
		*/
		// >"DMP"<LF>
		if (sendcmd(serverfd, "DMP") < 0) return -3;
		// <<ACK>
		if (getack(serverfd) < 0) return -4;
		// get pages
		retcode = 0;
		crccount = 0;
		while(1)
		{
			// read to ARCHIVE_PAGE with crc
			retcode = readWithCRC(serverfd, data, 267);
			onepage = (ARCHIVE_PAGE*)&data;
			// check crc
			if (retcode < 0)
			{
				printf("crc read error! [%d]\n", retcode);
				crccount = crccount + 1;
				// crc过多就取消下载
				if (crccount > CRCMAXCOUNT)
				{	
					if (sendcmd(serverfd, "\x1B") < 0) return -5;
					return -6;
				}
				// 重传
				if (sendcmd(serverfd, "\x21") < 0) return -7;
				continue;
			}
			// swap
			swapArchiveRecord (&onepage->record[i]);
			// <<0x00>
			// 1 Byte sequence number (starts at 0 and wraps from 255 back to 0)
/* 			if (TCPBlockRead(serverfd, &sequencenumberstep, 1) < 0) 
				{ printf("sequencenumber read error\n");return -7; }
			if ((sequencenumberstep-sequencenumber)!=1 
				|| (sequencenumberstep-sequencenumber)!=-255)
				{ printf("sequence error\n");return -8; }
			sequencenumber = sequencenumberstep; */
			if ((data[0]-sequencenumber)!=1
				|| (data[0]-sequencenumber)!=0
				|| (data[0]-sequencenumber)!=-255)
				{ printf("sequence error\n");return -8; }
			sequencenumber = data[0];
			// the 5 data record
			retcode = 0;
			// calculate the archive period
			// (Hour * 100) + minute.
			// Valid values by minutes are (1, 5, 10, 15, 30, 60, and 120).
			timespan = onepage->record[1].time - onepage->record[0].time;
			if (timespan = 100) timespan = 60;
			else if (timespan = 200) timespan = 120;
			// record every record in the page
			for (i=0; i<5; i++)
			{
				// 把每条记录中的数据解析出来写入downld02.txt文件中
				retcode = record(&onepage->record[i], FILE *downld02, timespan, bknTime);
				if (retcode < 0)
				{
					printf("sfsafsaf\n");
					return -9;
				}
			}
			retcode = updatetimestamp(onepage, timefile);
		}
		close(downld02);

	}
	else if (timefile != NULL)
	{
		/*
		>"DMPAFT"<LF>
		<<ACK>
		-- Send the Date and Time stamp --
		><0xC6><0xCE><0xA2><0x03>
		-- Send the calculated CRC 0xE2B4 –
		><0xE2><0xB4>
		<<ACK>
		-- Vantage responds with the number of pages it will send --
		<<0x0E><0x01><0x02><0x00><2 Bytes of CRC Data>
		-- Begin the download – Use <ESC> instead to cancel it
		><ACK>
		-- Block sequence number
		<<0x00>
		<<52 byte data record 0>
		<<52 byte data record 1>
		-- The next record is the first record with new data
		<<52 byte data record 2>
		<<52 byte data record 3>
		<<52 byte data record 4>
		<<4 unused bytes>
		<<2 byte CRC>
		-- At this point verify the CRC and send either <ACK>, <0x21>, or <ESC>
		*/
		// >"DMPAFT"<LF>
		sprintf(cmd, "DMPAFT\x0a");
		cmdlen = 7;
		retcode = TCPWrite(serverfd, cmd, cmdlen);
		if (retcode < 0)
		{
			printf("main: send cmd [%s] failed [%d]\n", cmd, retcode);
			return -4;
		}
		printf("main: cmd [%s] sent\n", cmd);
		// <<ACK>
		retcode = 0;
		memset(data, '\0', MAXSIZE);
		retcode = TCPBlockRead(serverfd, data, 1);
		if (retcode < 0)
		{
			printf("main: receive data failed [%d]\n",retcode);
			return -5;
		}
		printf("read [%x] size [%d]\n", (int)data[0], retcode);
		if (data[0] != 0x06)	//ack
		{
			printf("cmd not recognized \n");
			return -6;
		}
		/*
		-- Send the Date and Time stamp --
		><0xC6><0xCE><0xA2><0x03>
		-- Send the calculated CRC 0xE2B4 –
		><0xE2><0xB4>
		*/
		memset(cmd, '\0', MAXCMD);
		fread(cmd, 1, 6, timefile);
		fclose(timefile);
		cmdlen = 6;
		retcode = TCPWrite(serverfd, cmd, cmdlen);
		if (retcode < 0)
		{
			printf("cmd send error\n");
			return -7;
		}
		// <<ACK>
		retcode = 0;
		memset(data, '\0', MAXSIZE);
		retcode = TCPBlockRead(serverfd, data, 1);
		if (retcode < 0)
		{
			printf("main: receive data failed [%d]\n",retcode);
			return -5;
		}
		printf("read [%x] size [%d]\n", (int)data[0], retcode);
		if (data[0] != 0x06)	//ack
		{
			printf("cmd not recognized \n");
			return -6;
		}
		
	}
	
	
	return timespan;
}


